package com.kinglumi.ailighting.auth.service.auth.impl;

import com.kinglumi.ailighting.auth.bo.*;
import com.kinglumi.ailighting.auth.cache.Cache;
import com.kinglumi.ailighting.auth.constant.CacheName;
import com.kinglumi.ailighting.auth.constant.CommonConstant;
import com.kinglumi.ailighting.auth.constant.ServiceErrorEnum;
import com.kinglumi.ailighting.auth.email.EmailService;
import com.kinglumi.ailighting.auth.entity.UserDetail;
import com.kinglumi.ailighting.auth.entity.UserInfo;
import com.kinglumi.ailighting.auth.exception.BaseBusinessException;
import com.kinglumi.ailighting.auth.provider.AuthProvider;
import com.kinglumi.ailighting.auth.provider.JwtProvider;
import com.kinglumi.ailighting.auth.redis.IRedisCache;
import com.kinglumi.ailighting.auth.service.auth.AuthService;
import com.kinglumi.ailighting.auth.service.biz.UserInfoService;
import com.mysql.cj.util.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.Date;
import java.util.List;
import java.util.Objects;

@Slf4j
@Service
public class AuthServiceImpl implements AuthService {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private JwtProvider jwtProvider;

    @Autowired
    private Cache caffeineCache;

    @Autowired
    private UserInfoService userService;

    @Autowired
    private IRedisCache redisCache;

    @Autowired
    private EmailService emailService;


    @Override
    public ApiResult login(LoginInfo loginInfo) {
        log.debug("进入login方法");
        UsernamePasswordAuthenticationToken abstractAuthenticationToken = new UsernamePasswordAuthenticationToken(loginInfo.getUsername(), loginInfo.getPassword());
        // 2 认证
        Authentication authentication = this.authenticationManager.authenticate(abstractAuthenticationToken);
        // 3 保存认证信息
        SecurityContextHolder.getContext().setAuthentication(authentication);
        // 4 生成自定义token
        AccessToken accessToken = jwtProvider.createToken((UserDetails) authentication.getPrincipal());

        UserDetail userDetail = (UserDetail) authentication.getPrincipal();
        // 放入缓存
        caffeineCache.put(CacheName.USER, userDetail.getUsername(), userDetail);
        return ApiResult.ok(accessToken);
    }

    @Override
    public ApiResult logout() {
        caffeineCache.remove(CacheName.USER, AuthProvider.getLoginAccount());
        SecurityContextHolder.clearContext();
        return ApiResult.ok();
    }

    @Override
    public ApiResult refreshToken(String token) {
        AccessToken accessToken = jwtProvider.refreshToken(token);
        UserDetail userDetail = caffeineCache.get(CacheName.USER, accessToken.getLoginAccount(), UserDetail.class);
        caffeineCache.put(CacheName.USER, accessToken.getLoginAccount(), userDetail);
        return ApiResult.ok(accessToken);
    }

    @Override
    public ApiResult register(RegisterInfo registerInfo) {
        String userName = null;
        String verificationCode = registerInfo.getCode();
        if(!StringUtils.isNullOrEmpty(registerInfo.getEmail())){
            // 验证 验证码
            String email = registerInfo.getEmail();

            checkVerificationCode(email, verificationCode, ServiceErrorEnum.AUTH_VERIFICATION_EXPIRED, ServiceErrorEnum.AUTH_VERIFICATION_INCORRECT);
            // 判断是否邮箱，用户名 是否已经被使用
            this.isEmailExist(email);
            this.isUsernameExist(email);
            userName = email;
        }else if (!StringUtils.isNullOrEmpty(registerInfo.getTelephone())){
            String telephone = registerInfo.getTelephone();
            checkVerificationCode1(telephone, verificationCode, ServiceErrorEnum.AUTH_VERIFICATION_INCORRECT, ServiceErrorEnum.AUTH_VERIFICATION_INCORRECT);
            this.isTelephoneExist(telephone);
            userName = telephone;
        }

        //加密
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String encode = bCryptPasswordEncoder.encode(registerInfo.getPassword());

        UserInfo userInfo = new UserInfo();
        userInfo.setNickname(registerInfo.getNickname());
        userInfo.setPassword(encode);
        userInfo.setUsername(userName);
        if(!StringUtils.isNullOrEmpty(registerInfo.getEmail())){
            userInfo.setEmail(userName);
        }else if(!StringUtils.isNullOrEmpty(registerInfo.getTelephone())){
            userInfo.setTelephone(userName);
        }
        userInfo.setCreateTime(new Date());
        return ApiResult.ok(userService.insertSelectiveId(userInfo));
    }

    @Override
    public Boolean isEmailExist(String email) {
        // 根据邮箱验证用户是否注册
        UserInfo info1 = new UserInfo();
        info1.setEmail(email);
        List<UserInfo> infoList = userService.selectList(info1);
        if (!CollectionUtils.isEmpty(infoList)) {
            throw new BaseBusinessException(ServiceErrorEnum.AUTH_EMAIL_USED);
        }
        return true;
    }

    @Override
    public Boolean isUsernameExist(String username) {
        // 根据邮箱验证用户是否注册
        UserInfo info1 = new UserInfo();
        info1.setUsername(username);
        List<UserInfo> infoList = userService.selectList(info1);
        if (!CollectionUtils.isEmpty(infoList)) {
            throw new BaseBusinessException(ServiceErrorEnum.AUTH_EMAIL_USED);
        }
        return true;
    }

    public Boolean isTelephoneExist(String telephone) {
        // 根据邮箱验证用户是否注册
        UserInfo info1 = new UserInfo();
        info1.setTelephone(telephone);
        List<UserInfo> infoList = userService.selectList(info1);
        if (!CollectionUtils.isEmpty(infoList)) {
            throw new BaseBusinessException(ServiceErrorEnum.AUTH_TELEPHONE_USED);
        }
        return true;
    }

    @Override
    public String emailCode(String email) {
        // 根据用户名验证用户
//        UserInfo info1 = new UserInfo();
//        info1.setEmail(email);
//        UserInfo userInfo = userService.selectOne(info1);
//        if(userInfo == null){
//            throw new BaseBusinessException(ServiceErrorEnum.AUTH_EMAIL_INCORRECT);
//        }
        Object o = redisCache.get(CommonConstant.RedisKeys.EMAIL_SEND_TIME + email);
        if (Objects.nonNull(o) && (Integer) o >= 50) {
            throw new BaseBusinessException(ServiceErrorEnum.AUTH_NOT_END_MAIL);
        }
        String code = (String) emailService.sendEmail(email);
        this.redisCache.incr(CommonConstant.RedisKeys.EMAIL_SEND_TIME + email, 1);
        // 10 分钟后过期
        this.redisCache.expire(CommonConstant.RedisKeys.EMAIL_SEND_TIME + email, 600);
        // 一小时 后过期
        this.redisCache.set(CommonConstant.RedisKeys.EMAIL_CODE + email, code, 3600);
        return code;
    }

    @Override
    public String emailCodeByScope(String email, Integer scope) {
        // 根据用户名验证用户
//        UserInfo info1 = new UserInfo();
//        info1.setEmail(email);
//        UserInfo userInfo = userService.selectOne(info1);
//        if(userInfo == null){
//            throw new BaseBusinessException(ServiceErrorEnum.AUTH_EMAIL_INCORRECT);
//        }
        Object o = redisCache.get(CommonConstant.RedisKeys.EMAIL_SEND_TIME + email);
        if (Objects.nonNull(o) && (Integer) o >= 50) {
            throw new BaseBusinessException(ServiceErrorEnum.AUTH_NOT_END_MAIL);
        }
        String code;
        if(scope != null && scope == 2){
            code = (String) emailService.sendEmail(email);
        }else {
            code = (String) emailService.sendEmailGuonei(email);
        }

        this.redisCache.incr(CommonConstant.RedisKeys.EMAIL_SEND_TIME + email, 1);
        // 10 分钟后过期
        this.redisCache.expire(CommonConstant.RedisKeys.EMAIL_SEND_TIME + email, 600);
        // 一小时 后过期
        this.redisCache.set(CommonConstant.RedisKeys.EMAIL_CODE + email, code, 3600);
        return code;
    }

    @Override
    public String codeByScopeAndApp(String email, Integer scope, Integer app) {
        Object o = redisCache.get(CommonConstant.RedisKeys.EMAIL_SEND_TIME + email);
        if (Objects.nonNull(o) && (Integer) o >= 50) {
            throw new BaseBusinessException(ServiceErrorEnum.AUTH_NOT_END_MAIL);
        }
        String code;
        if(scope != null && scope == 2){
            code = (String) emailService.sendEmaiAndlApp(email, app);
        }else {
            code = (String) emailService.sendEmaiAndlApp(email, app);
        }

        this.redisCache.incr(CommonConstant.RedisKeys.EMAIL_SEND_TIME + email, 1);
        // 10 分钟后过期
        this.redisCache.expire(CommonConstant.RedisKeys.EMAIL_SEND_TIME + email, 600);
        // 一小时 后过期
        this.redisCache.set(CommonConstant.RedisKeys.EMAIL_CODE + email, code, 3600);
        return code;
    }

    /**
     * 检查验证码
     *
     * @param email
     * @param verificationCode
     */
    private void checkVerificationCode(String email, String verificationCode, ServiceErrorEnum one, ServiceErrorEnum two) {
        Object code = this.redisCache.get(CommonConstant.RedisKeys.EMAIL_CODE + email);
        if (Objects.isNull(code)) {
            throw new BaseBusinessException(one);
        }
        if (!verificationCode.equals(code)) {
            throw new BaseBusinessException(two);
        }
        // 删除使用过的验证码
        this.redisCache.del(CommonConstant.RedisKeys.EMAIL_CODE + email);
    }

    private void checkVerificationCode1(String telephone, String verificationCode, ServiceErrorEnum one, ServiceErrorEnum two) {
        Object code = this.redisCache.get("smsCode" + telephone);
        if (Objects.isNull(code)) {
            throw new BaseBusinessException(one);
        }
        if (!verificationCode.equals(code)) {
            throw new BaseBusinessException(two);
        }
        // 删除使用过的验证码
        this.redisCache.del("smsCode" + telephone);
    }

    @Override
    public ApiResult resetPassword(ResetPassword registerInfo) {
        UserInfo one = null;
        String verificationCode = registerInfo.getCode();
        if(!StringUtils.isNullOrEmpty(registerInfo.getEmail())){
            String email = registerInfo.getEmail();
            // 检查验证码
            checkVerificationCode(email, verificationCode, ServiceErrorEnum.AUTH_VERIFICATION_EXPIRED, ServiceErrorEnum.AUTH_VERIFICATION_INCORRECT);
            UserInfo userInfo = new UserInfo();
            userInfo.setEmail(email);
            one = userService.selectOne(userInfo);
            if (Objects.isNull(one)) {
                throw new BaseBusinessException(ServiceErrorEnum.AUTH_EMAIL_INCORRECT);
            }
        }else if (!StringUtils.isNullOrEmpty(registerInfo.getTelephone())) {
            String telephone = registerInfo.getTelephone();
            checkVerificationCode1(telephone, verificationCode, ServiceErrorEnum.AUTH_VERIFICATION_INCORRECT, ServiceErrorEnum.AUTH_VERIFICATION_INCORRECT);
            UserInfo userInfo = new UserInfo();
            userInfo.setTelephone(telephone);
            one = userService.selectOne(userInfo);
            if (Objects.isNull(one)) {
                throw new BaseBusinessException(ServiceErrorEnum.AUTH_TELEPHONE_INCORRECT);
            }
        }
//        else{
//            throw new BaseBusinessException(ServiceErrorEnum.USERNAME_INCORRECT);
//        }
        //加密
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String encode = bCryptPasswordEncoder.encode(registerInfo.getPassword());
        one.setPassword(encode);
        userService.updateSelectiveById(one);
        return ApiResult.ok();
    }

    @Override
    public ApiResult forgetPassword(ResetPassword registerInfo) {
        return resetPassword(registerInfo);
    }

    @Override
    public ApiResult updatePassword(UpdatePassword updatePassword) {
        if (Objects.isNull(updatePassword)) {
            return null;
        }
        if (Objects.isNull(updatePassword.getEmail())
                && (Objects.isNull(updatePassword.getUsername()))) {
            return null;
        }
        UserInfo userInfo = new UserInfo();
        if (!StringUtils.isNullOrEmpty(updatePassword.getEmail())) {
            userInfo.setEmail(updatePassword.getEmail());
        } else if (!StringUtils.isNullOrEmpty(updatePassword.getUsername())) {
            userInfo.setUsername(updatePassword.getUsername());
        }
        UserInfo one = userService.selectOne(userInfo);
//        if (Objects.isNull(one)) {
//            throw new BaseBusinessException(ServiceErrorEnum.USERNAME_INCORRECT);
//        }
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        boolean matches = bCryptPasswordEncoder.matches(updatePassword.getOldPassword(), one.getPassword());
        if (!matches) {
            throw new BaseBusinessException(ServiceErrorEnum.AUTH_UPDATE_INCORRECT);
        }
        //加密
        BCryptPasswordEncoder newtPasswordEncoder = new BCryptPasswordEncoder();
        String encode = newtPasswordEncoder.encode(updatePassword.getNewPassword());
        one.setPassword(encode);
        userService.updateSelectiveById(one);
        return ApiResult.ok();
    }
}
